package priv.wangy.micro.common.po.valid.validator;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import priv.wangy.micro.common.po.valid.DateRange;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author zhangjq
 * @since 2023-11-13
 */
public class DateRangeValidator implements ConstraintValidator<DateRange, String[]> {

    private int maxIntMon;

    private int maxIntDay;

    private boolean nullable;

    private boolean time;

    private static final Pattern PATTERN = Pattern.compile("\\d{4}-\\d{2}-\\d{2}");

    private static final Pattern PATTERN_WITH_TIME = Pattern.compile("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}");

    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    private static final DateTimeFormatter FORMATTER_WITH_TIME = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Override
    public void initialize(DateRange constraintAnnotation) {
        ConstraintValidator.super.initialize(constraintAnnotation);
        maxIntMon = constraintAnnotation.maxIntMon();
        maxIntDay = constraintAnnotation.maxIntDay();
        nullable = constraintAnnotation.nullable();
        time = constraintAnnotation.time();
    }

    @Override
    public boolean isValid(String[] strings, ConstraintValidatorContext constraintValidatorContext) {
        if (nullable && Objects.isNull(strings)) {
            return true;
        }
        if (Objects.isNull(strings) || strings.length != 2) {
            return false;
        }
        for (String string : strings) {
            Matcher matcher = time ? PATTERN_WITH_TIME.matcher(string) : PATTERN.matcher(string);
            if (!matcher.matches()) {
                return false;
            }
        }
        LocalDateTime start, end;
        if (time) {
            start = LocalDateTime.parse(strings[0], FORMATTER_WITH_TIME);
            end = LocalDateTime.parse(strings[1], FORMATTER_WITH_TIME);
        } else {
            start = LocalDate.parse(strings[0], FORMATTER).atTime(LocalTime.MIN);
            end = LocalDate.parse(strings[1], FORMATTER).atTime(LocalTime.MAX);
        }

        if (maxIntMon > 0) {
            long intMon = ChronoUnit.MONTHS.between(start, end);
            if (intMon >= maxIntMon) {
                return false;
            }
        }
        if (maxIntDay > 0) {
            long intDay = ChronoUnit.DAYS.between(start, end);
            if (intDay >= maxIntDay) {
                return false;
            }
        }
        return start.isBefore(end);
    }
}
